package main

import (
	_ "embed"
	"fmt"
	"math/rand/v2"
	"os"
	"path/filepath"
	"slices"
	"sort"
	"strings"
	"sync/atomic"
	"time"

	"fyne.io/fyne/v2"
	"fyne.io/fyne/v2/app"

	"github.com/adrg/sysfont"
	"github.com/gordonklaus/portaudio"

	"fyne.io/fyne/v2/container"
	"fyne.io/fyne/v2/widget"
)

func getFont() string {
	words := []string{"song", "fangsong", "kai", "hei"}
	exclude := []string{"simsun.ttf", "simkai.ttf", "simhei.ttf"}
	//words := []string{"kaiti"}
	opts := sysfont.FinderOpts{Extensions: []string{".ttf", ".ttc"}}
	finder := sysfont.NewFinder(&opts)

	for _, f := range finder.List() {
		//println(f.Name)
		for _, w := range words {
			if strings.Index(strings.ToLower(f.Name), w) >= 0 {
				if slices.Contains(exclude, filepath.Base(f.Filename)) {
					continue
				}
				//println(w, f.Name, f.Filename)
				return f.Filename
			}
		}
	}
	return ""
}

func init() {
	fontpath := getFont()
	os.Setenv("FYNE_FONT", fontpath)
}

//go:embed icon.jpeg
var icon1 []byte

type LineData struct {
	Name     string
	Play     *widget.Button
	Del      *widget.Button
	Status   *widget.Label
	FileName string
}

var table1 *widget.Table
var constButtons []*widget.Button
var lines = []*LineData{}
var recordFlag = &atomic.Bool{}

func getTableCell(bt *widget.Button) {
	ret := &widget.TableCellID{Row: -1, Col: -1}
	for i, _ := range lines {
		if lines[i].Play == bt {
			ret.Row = i
			ret.Col = 0

		}
		if lines[i].Del == bt {

			ret.Row = i
			ret.Col = 1

		}
	}
	table1.Select(*ret)
	if ret.Row == -1 {
		ret.Row = 0
		ret.Col = 0
		table1.ScrollTo(*ret)
		return
	}

	if ret.Row < len(lines)-1 {
		ret.Row += 1
	}
	table1.ScrollTo(*ret)

}

func main() {
	recordFlag.Store(false)
	defer input.Stop()
	defer input.Close()
	defer portaudio.Terminate()
	// 程序结束时终止PortAudio

	var name string
	var filename string
	ctrl := make(chan int8)
	defer close(ctrl)
	defer close(playCtrlStop)
	var num int

	os.MkdirAll("audios", os.ModePerm)

	dir1, err := os.Open("audios")
	if err == nil {
		audios, _ := dir1.Readdirnames(-1)
		sort.Strings(audios)

		if len(audios) > 0 {

			for _, v := range audios {
				if !strings.HasSuffix(v, ".mp3") {
					continue
				}
				num++
				name := fmt.Sprintf("%v%03d", T("audio"), num)
				data := &LineData{Name: name, FileName: v, Status: widget.NewLabel("-")}
				lines = append(lines, data)
			}
		}
	}

	app := app.NewWithID("ZiZhuMoXie")
	//app.Settings().SetTheme(GetMyTheme())
	w := app.NewWindow(T("ZiZhuMoXie"))
	w.Canvas().SetOnTypedKey(func(ke *fyne.KeyEvent) {

		switch ke.Name {
		case fyne.KeySpace:
			bts := getButtonList()
			bts[curButton].Tapped(nil)
		case fyne.KeyEnter:
			bts := getButtonList()
			bts[curButton].Tapped(nil)
		case fyne.KeyReturn:
			bts := getButtonList()
			bts[curButton].Tapped(nil)
		case fyne.KeyDown:
			getNextButton()
		case fyne.KeyRight:
			getNextButton()
		case fyne.KeyUp:
			getLastButton()
		case fyne.KeyLeft:
			getLastButton()
		}
	})
	btRecord := widget.NewButton(T("record"), nil)
	var autoPlay = &atomic.Bool{}
	chkBox := widget.NewCheck(T("PlayImmediately"), func(b bool) {
		autoPlay.Store(b)
	})
	chkBox.SetChecked(true)
	bts1 := container.NewHBox(btRecord, chkBox)
	constButtons = append(constButtons, btRecord)

	table1 = widget.NewTable(func() (int, int) {
		return len(lines), 3
	}, func() fyne.CanvasObject {
		ret := container.NewCenter(widget.NewLabel("-"))
		//ret.Resize(fyne.NewSize(240, 18))
		return ret
	}, func(lii widget.TableCellID, co fyne.CanvasObject) {

		switch lii.Col {
		case 0:
			//第一列，音频名，播放按钮
			txt1 := widget.NewButton(lines[lii.Row].Name, func() {

				go func() {
					if lines[lii.Row].Status.Text == "Playing" {
						stopPlay()
						return
					}
					if playFlag.Load() {
						app.SendNotification(fyne.NewNotification(T("ZiZhuMoXie"), T("waitPlay")))
						return
					}
					if recordFlag.Load() {
						app.SendNotification(fyne.NewNotification(T("ZiZhuMoXie"), T("waitRecord")))
						return
					}
					status := lines[lii.Row].Status
					status.SetText("Playing")
					//显示“停止”
					lines[lii.Row].Play.SetText(T("stop"))
					err := playAudio(lines[lii.Row].FileName)
					//显示音频名字
					lines[lii.Row].Play.SetText(lines[lii.Row].Name)
					if err == nil {
						if lii.Row < len(lines) {
							status.SetText("X")
						}

					}

				}()

			})
			co.(*fyne.Container).RemoveAll()
			co.(*fyne.Container).Add(txt1)
			lines[lii.Row].Play = txt1
		case 1:
			//第二列，删除按钮
			del1 := widget.NewButton(T("del"), func() {
				// if playFlag.Load() {
				// 	app.SendNotification(fyne.NewNotification("自助默写", "请等待播放结束再点击。"))
				// 	return
				// }
				name := filepath.Join("audios", lines[lii.Row].FileName)
				os.Remove(name)

				lines = slices.Delete(lines, lii.Row, lii.Row+1)

				table1.Refresh()

			})
			co.(*fyne.Container).RemoveAll()
			co.(*fyne.Container).Add(del1)
			lines[lii.Row].Del = del1
		case 2:
			//第三列，状态标志，'X':已播放，'Playing':正在播放，'-':未播放

			co.(*fyne.Container).RemoveAll()
			co.(*fyne.Container).Add(lines[lii.Row].Status)

		}

	})

	resIcon := fyne.NewStaticResource("icon.jpeg", icon1)
	app.SetIcon(resIcon)

	var ticker = time.NewTicker(time.Second)
	var timer bool = false
	go func() {
		//当 timer==true 时，进行1秒1次循环计数，更新“录音”按钮的文字为“停止 - N”
		n := 0
		for {
			<-ticker.C
			if timer {
				n += 1
				btRecord.SetText(fmt.Sprintf("%v - %v", T("stop"), n))
				btRecord.Refresh()
			} else {
				n = 0
				ticker.Stop()
			}
		}
	}()

	btRecord.OnTapped = func() {
		if recordFlag.Load() {
			//如果正在录音，停止录音，更新文字为“录音”，把新的音频加入表格

			timer = false

			stopRecord(ctrl)
			btRecord.SetText(T("record"))

			data := &LineData{Name: name, Status: widget.NewLabel("-"), FileName: filename}
			lines = append(lines, data)
			table1.Refresh()

			if autoPlay.Load() {
				go func() {
					playAudio(filename)

				}()
			}

		} else {
			//开始录音
			num += 1
			name = fmt.Sprintf("%v%03d", T("audio"), num)
			filename = fmt.Sprintf("a%v.mp3", time.Now().Unix())
			timer = true
			btRecord.Disable()
			stopPlayForRecord()
			ticker.Reset(time.Second)
			go startRecord(filename, ctrl)
			btRecord.SetText(T("stop") + " - 0")
			btRecord.Enable()
		}
	}

	btCleanFlag := widget.NewButton(T("cleanFlag"), func() {
		//把所有音频的状态标志设置为“-”
		for i, _ := range lines {
			lines[i].Status.SetText("-")
		}
	})
	constButtons = append(constButtons, btCleanFlag)

	btClean := widget.NewButton(T("delAll"), func() {
		//删除所有音频
		os.RemoveAll("audios")

		lines = []*LineData{}
		table1.Refresh()

		num = 0
		curButton = -1
	})
	constButtons = append(constButtons, btClean)

	btRand := widget.NewButton(T("rearrange"), func() {
		//随机排序所有音频
		lines = randStrings(lines)
		for i, _ := range lines {
			lines[i].Status.SetText("-")
		}
		table1.Refresh()

	})
	constButtons = append(constButtons, btRand)
	hbox1 := container.NewHBox(btCleanFlag, btClean, btRand)

	scroll1 := container.NewVScroll(table1)
	scroll1.SetMinSize(fyne.NewSize(280, 520))
	table1.SetColumnWidth(0, 140)
	table1.SetColumnWidth(1, 80)
	table1.SetColumnWidth(2, 60)

	box1 := container.NewVBox(bts1, hbox1, scroll1)

	w.SetIcon(resIcon)
	w.SetContent(box1)
	w.Resize(fyne.NewSize(300, 550))

	w.SetMaster()
	w.RequestFocus()

	w.SetOnClosed(func() {
		AudioOnExit()
		app.Quit()
	})

	w.CenterOnScreen()
	table1.ScrollTo(widget.TableCellID{Row: len(lines) - 1, Col: 0})

	w.ShowAndRun()
}

func ErrPanic(err error) {
	if err != nil {
		panic(err)
	}
}

func randStrings(s []*LineData) []*LineData {
	var length = len(s)
	ret := make([]*LineData, length)
	for i := 0; i < length; i++ {
		n := rand.IntN(length - i)
		ret[i] = s[n]
		s = slices.Delete(s, n, n+1)
	}
	return ret
}

func getButtonList() []*widget.Button {
	res := make([]*widget.Button, len(constButtons)+len(lines)*2)
	for i, _ := range constButtons {
		res[i] = constButtons[i]
	}
	a := len(constButtons)
	for i, _ := range lines {
		res[2*i+a] = lines[i].Play
		res[2*i+a+1] = lines[i].Del
	}

	return res
}

var curButton int = -1

func getNextButton() {
	btList := getButtonList()
	if curButton > -1 && curButton < len(btList) {
		btList[curButton].FocusLost()
	}

	curButton++
	curButton = curButton % len(btList)

	getTableCell(btList[curButton])

	btList[curButton].FocusGained()
}

func getLastButton() {
	btList := getButtonList()
	if curButton > -1 {
		curButton = curButton % len(btList)
		btList[curButton].FocusLost()
	}

	if curButton > 0 {
		curButton--
	} else {
		curButton = len(btList) - 1
	}

	getTableCell(btList[curButton])

	btList[curButton].FocusGained()

}
